import base64
import json
import random
import time

import ddddocr
import execjs
import requests
from PIL import Image


class Slide:
    def __init__(self):
        self.one_url = 'https://www.geetest.com/demo/gt/register-test'
        self.two_url = 'https://api.geevisit.com/ajax.php'
        self.th_url = 'https://api.geevisit.com/get.php'
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/119.0.0.0 Safari/537.36",
            "Referer": "https://www.geetest.com/",
        }
        self.challenge = None
        self.gt = None
        self._custom_url = "http://api.jfbym.com/api/YmServer/customApi"
        self._headers = {
            'Content-Type': 'application/json'
        }
        self.gj = []

    def one_request(self):
        params = {
            't': str(int(time.time() * 1000))
        }
        res = requests.get(self.one_url, headers=self.headers, params=params)
        self.challenge = res.json()['challenge']
        self.gt = res.json()['gt']

    def two_request(self):
        params2 = {
            "gt": self.gt,
            "challenge": self.challenge,
            "lang": "zh-cn",
            "pt": "0",
            "client_type": "web",
            "w": "",
            "callback": "geetest_" + str(int(time.time() * 1000))
        }
        requests.get(self.two_url, headers=self.headers, params=params2)

        params3 = {
            "is_next": "true",
            "type": "slide3",
            "gt": self.gt,
            "challenge": self.challenge,
            "lang": "zh-cn",
            "https": "true",
            "protocol": "https://",
            "offline": "false",
            "product": "embed",
            "api_server": "api.geevisit.com",
            "isPC": "true",
            "autoReset": "true",
            "width": "100%",
            "callback": "geetest_" + str(int(time.time() * 1000))
        }

        res = requests.get(self.th_url, headers=self.headers, params=params3).text
        data = json.loads(res.split('(')[1].split(')')[0])
        # print(json.loads(res[1:-1]))
        bg = data['bg']
        slice = data['slice']
        c = data['c']
        s = data['s']
        self.challenge = data['challenge']
        return bg, slice, c, s

    def get_xy(self):
        det = ddddocr.DdddOcr(det=False, ocr=False)

        with open('bg.png', 'rb') as f:
            target_bytes = f.read()

        with open('slice.png', 'rb') as f:
            background_bytes = f.read()

        res = det.slide_match(target_bytes, background_bytes, simple_target=True)
        self.x = res['target'][0]

    def get_slide_track(self, distance):
        slide_track = [
            [random.randint(-40, -30), random.randint(-35, -25), 0],
            [0, 0, 0],
        ]
        x = 0
        y = 0  # 数组的y值
        t = random.randint(100, 150)
        while True:
            i = random.choices([0, 1, 2, random.randint(3, 7)], weights=[3, 5, 4, 1], k=1)[0]  # 随机移动距离
            # i = random.randint(0, 2)
            x += i

            # 边界判断
            x = distance if x >= distance else x

            # 时间变化
            if x > distance - 3:
                # 模拟快到目标细微的调整
                t += random.randint(80, 180)
            else:
                # 模拟手抖动情况
                if random.choices([0, 1], weights=[15, 2], k=1)[0] == 0:
                    t += random.randint(7, 9)
                else:
                    t += random.randint(10, 50)

            if x > distance * 0.95:
                y += random.choices([0, 1], weights=[3, 1], k=1)[0]
            elif x > distance * 0.7:
                y += random.choices([0, 1], weights=[4, 1], k=1)[0]

            slide_track.append([x, y, t])

            if distance - x <= 0:
                # 模拟松开手停顿情况
                slide_track.append([x, y, t + random.randint(200, 400)])
                self.gj = slide_track
                print(slide_track)
                print(len(slide_track))
                break

    def get_slide_track2(self, x):
        # 初始化轨迹列表
        slide_track = [
            [random.randint(-50, -20), random.randint(-50, -20), 0],
            [0, 0, 0],
        ]

        if x < 100:
            move_section = 1  # 如果移动距离小于100 那么move次数为x加上 7到20之间的数
        else:
            move_section = 2  # 如果移动距离小于100 那么move次数为x加上 2乘 7到20之间的数

        up_down = random.randint(0, 1)  # 确定一个方向 x大于0或x小于0
        y = 0  # 数组的y值
        t = random.randint(100, 180)  # 初始时间 即为第二个数组的时间  后续时间累加操作就可以了
        count = 0
        flag = 0
        repetition = int(x / 4)  # 重复x出现的个数
        first_count = random.randint(6, 10)  # 前面y为0的数组个数
        for i in range(x * random.randint(move_section * 7, move_section * 21)):  # move_section 在这里起作用
            if i + 1 > x:  # 如果i+1要等于x 或者小于x 但这里基本上都是等于x
                break
            if up_down == 0:  # up_down如果大于0 那么这个轨迹就是y增轨迹
                if i > first_count:
                    if count == 0:
                        y += random.randint(0, 1)
                        count += 1
                    if flag > random.randint(8, 10):
                        count = 0
                        flag = 0
                    if i + 1 > int(x / 5) * 4:
                        t += random.randint(20, 70)
                    elif i + 1 > x - 3:
                        t += random.randint(80, 180)
                    else:
                        t += random.randint(0, 5)
                    slide_track.append([i + 1, y, t])
                    flag += 1
                    if random.randint(0, 1):
                        if repetition:
                            slide_track.append([i + 1, y, t + random.randint(0, 3)])
                            flag += 1
                            repetition -= 1
                else:  # 前面几个数组y都为0
                    t += random.randint(0, 5)
                    slide_track.append([i + 1, y, t])
                    if random.randint(0, 1):
                        if repetition:
                            slide_track.append([i + 1, y, t + random.randint(0, 3)])
                            repetition -= 1

            if up_down == 1:  # up_down如果小于0 那么这个轨迹就是y减轨迹
                if i > first_count:
                    if count == 0:
                        y -= random.randint(0, 1)
                        count += 1
                    if flag > random.randint(8, 10):
                        count = 0
                        flag = 0
                    if i + 1 > int(x / 5) * 4:
                        t += random.randint(7, 40)
                    elif i + 1 > x - 3:
                        t += random.randint(80, 180)
                    else:
                        t += random.randint(0, 5)
                    slide_track.append([i + 1, y, t])
                    flag += 1
                    if random.randint(0, 1):
                        if repetition:
                            slide_track.append([i + 1, y, t + random.randint(0, 3)])
                            flag += 1
                            repetition -= 1
                else:
                    t += random.randint(0, 5)
                    slide_track.append([i + 1, y, t])
                    if random.randint(0, 1):
                        if repetition:
                            slide_track.append([i + 1, y, t + random.randint(0, 3)])
                            repetition -= 1

        print(slide_track)
        self.gj = slide_track

    @classmethod
    def __ease_out_expo(cls, sep):
        if sep == 1:
            return 1
        else:
            return 1 - pow(2, -10 * sep)

    def get_slide_track3(self, distance):
        """
        根据滑动距离生成滑动轨迹
        :param distance: 需要滑动的距离
        :return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
            x: 已滑动的横向距离
            y: 已滑动的纵向距离, 除起点外, 均为0
            t: 滑动过程消耗的时间, 单位: 毫秒
        """

        if not isinstance(distance, int) or distance < 0:
            raise ValueError(f"distance类型必须是大于等于0的整数: distance: {distance}, type: {type(distance)}")
        # 初始化轨迹列表
        slide_track = [
            [random.randint(-50, -10), random.randint(-50, -10), 0],
            [0, 0, 0],
        ]
        # 共记录count次滑块位置信息
        count = 30 + int(distance / 2)
        # 初始化滑动时间
        t = random.randint(50, 100)
        # 记录上一次滑动的距离
        _x = 0
        _y = 0
        for i in range(count):
            # 已滑动的横向距离
            x = round(self.__ease_out_expo(i / count) * distance)
            # 滑动过程消耗的时间
            t += random.randint(10, 20)
            if x == _x:
                continue
            _y += random.choices([0, 1], weights=[9, 1], k=1)[0]
            slide_track.append([x, _y, t])
            _x = x
        slide_track.append(slide_track[-1])
        self.gj = slide_track

    def there_request(self, bg, slice, c, s):
        res = requests.get('https://static.geetest.com/' + bg, headers=self.headers)
        open('bg.png', 'wb').write(res.content)
        self.image_restore('bg.png')

        res = requests.get('https://static.geetest.com/' + slice, headers=self.headers)
        slice_content = res.content
        open('slice.png', 'wb').write(slice_content)
        # self.image_restore('slice.png')

        with open('bg.png', 'rb') as f:
            bg_content = f.read()
        # with open('slice.png', 'rb') as f:
        #     slice_content = f.read()

        # distance = self.float_verify(slice_content, bg_content)
        self.get_xy()
        distance = self.x
        # distance = 112

        self.get_slide_track3(int(distance))

        # self.gj = [[-39,-34,0],[0,0,0],[1,1,104],[3,1,111],[7,1,118],[11,1,126],[18,1,135],[45,1,169],[53,1,177],[61,1,183],[68,1,191],[74,1,200],[81,1,207],[86,1,215],[91,1,223],[96,1,231],[100,1,239],[105,1,247],[109,1,255],[113,1,262],[117,1,271],[122,1,279],[126,1,287],[129,1,295],[133,1,303],[137,1,311],[139,1,319],[141,2,327],[143,2,334],[145,3,343],[145,3,351],[146,3,359],[147,3,375],[147,3,447],[148,3,456],[149,4,462],[150,4,471],[152,5,486],[153,5,495],[154,5,503],[155,5,511],[157,6,518],[159,7,527],[159,7,542],[160,7,606],[161,7,1111],[162,8,1126],[163,8,1143],[165,9,1151],[165,9,1159],[166,9,1166],[166,9,1479]]
        #
        # self.challenge = "4d138d1864fc66a1b842dc16665c865ago"
        # s = "28454c6c"

        js = execjs.compile(open('滑块验证码.js', encoding='utf-8').read())
        print("传递参数：", distance, self.challenge, self.gt, c, s)
        w = js.call('get_w', distance, self.challenge, self.gt, c, s, self.gj)
        print(w)
        return w

    def get_data(self, w):
        params = {
            "gt": self.gt,
            "challenge": self.challenge,
            "lang": "zh-cn",
            "$_BCN": "0",
            "client_type": "web",
            "w": w,
            "callback": "geetest_" + str(int(time.time() * 1000))
        }
        url = "https://api.geevisit.com/ajax.php"
        # time.sleep(2)
        response = requests.get(url, params=params, headers=self.headers)
        res = json.loads(response.text.split('(')[1].split(')')[0])
        print("响应参数：", res)
        validate = ''
        if res['message'] == 'success':
            validate = res['validate']
        return validate

    def image_restore(self, img):
        image = Image.open(img)
        standard_img = Image.new("RGBA", (260, 160))
        position = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43,
                    42,
                    12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17]
        s, u = 80, 10
        for c in range(52):
            a = position[c] % 26 * 12 + 1
            b = s if position[c] > 25 else 0
            im = image.crop(box=(a, b, a + 10, b + 80))
            standard_img.paste(im, box=(c % 26 * 10, 80 if c > 25 else 0))

        standard_img.save(img)

    def float_verify(self, slide_image, background_image, extra=None, verify_type="20111"):
        payload = {
            "slide_image": base64.b64encode(slide_image).decode(),
            "background_image": base64.b64encode(background_image).decode(),
            "token": '7pE3vAgqNa3cDZ4iQULCogDd6oupapu9BVOLI9Fj32U',
            "type": verify_type
        }
        if extra:
            payload['extra'] = extra
        resp = requests.post(self._custom_url, headers=self._headers, data=json.dumps(payload))
        print(resp.text)
        return resp.json()['data']['data']

    def validate_request(self, validate):
        data = {
            "geetest_challenge": self.challenge,
            "geetest_validate": validate,
            "geetest_seccode": validate + "|jordan"
        }
        url = "https://www.geetest.com/demo/gt/validate-test"
        response = requests.post(url, headers=self.headers, data=data)
        print(response.text)

    def main(self):
        self.one_request()
        bg, slice, c, s = self.two_request()
        w = self.there_request(bg, slice, c, s)
        validate = self.get_data(w)
        self.validate_request(validate)


if __name__ == '__main__':
    jy = Slide()
    jy.main()

